/*
 -----------------------------------------------------------------------------
 This source file is part of FolderProtector
 Copyright (c) 2011, Vyacheslav Chigrin, folderprotector (at) gmail.com  All rights reserved.
 Send your commends
     ---The BSD 3-Clause License---
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution.
* Neither the name of the Folder Protector project nor the names of its contributors
  may be used to endorse or promote products derived from this software without specific
  prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
  SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  -----------------------------------------------------------------------------
*/
#include "stdafx.h"
#include "CommonKMUM.h"
#include "PathsMatcher.h"
#include <new.h>

#ifdef __cplusplus
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING  RegistryPath);
#endif

void DriverUnload(DRIVER_OBJECT* pDrvObj);
FLT_PREOP_CALLBACK_STATUS FLTAPI PreCreate(PFLT_CALLBACK_DATA Data,PCFLT_RELATED_OBJECTS FltObjects,PVOID *pCompletionContext);
FLT_POSTOP_CALLBACK_STATUS FLTAPI PostDirectoryControl(PFLT_CALLBACK_DATA Data,PCFLT_RELATED_OBJECTS FltObjects,PVOID CompletionContext,FLT_POST_OPERATION_FLAGS Flags);
NTSTATUS FLTAPI FilterUnload (FLT_FILTER_UNLOAD_FLAGS Flags);
NTSTATUS OnDeviceControl(DEVICE_OBJECT* pDevice,IRP* pIrp);
NTSTATUS OnCreateClose(DEVICE_OBJECT* pDevice,IRP* pIrp);
NTSTATUS LoadConfiguration(DEVICE_OBJECT* pDevice);

PFLT_FILTER g_pFilterData=NULL;
DEVICE_OBJECT* g_pControlDevice=NULL;
struct DEVICE_EXT_DATA
{
    UNICODE_STRING RegistryPath;
    CPathsMatcher HiddenPathsMatcher;
};

#define PARAMETERS_STRING L"\\Parameters"

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING  RegistryPath)
{
    DriverObject->DriverUnload=DriverUnload;
    KdPrint(("Driver entry called..."));
    FLT_REGISTRATION FilterRegistration;

    memset(&FilterRegistration,0,sizeof(FilterRegistration));
    FilterRegistration.Size=sizeof(FilterRegistration);
    FilterRegistration.Version=FLT_REGISTRATION_VERSION;
    FilterRegistration.Flags=0;
    //we do not use any contexts yet
    FLT_CONTEXT_REGISTRATION ContextReg[]=
    {
       {FLT_CONTEXT_END}
    };
    FLT_OPERATION_REGISTRATION OperationReg[]=
    {
        {IRP_MJ_DIRECTORY_CONTROL,0,NULL,PostDirectoryControl,NULL},
        {IRP_MJ_CREATE,0,PreCreate,NULL,NULL},
        {IRP_MJ_OPERATION_END}
    };
    FilterRegistration.ContextRegistration=ContextReg;
    FilterRegistration.OperationRegistration=OperationReg;
    FilterRegistration.FilterUnloadCallback=FilterUnload;
    NTSTATUS status;
    do
    {
        status=FltRegisterFilter(DriverObject,&FilterRegistration,&g_pFilterData);
        if(!NT_SUCCESS(status))
        {
            KdPrint(("Failed register filter %08X",status));
            break;
        }
        DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=OnDeviceControl;
        DriverObject->MajorFunction[IRP_MJ_CREATE]=OnCreateClose;
        DriverObject->MajorFunction[IRP_MJ_CLOSE]=OnCreateClose;
        UNICODE_STRING DeviceName;
        RtlInitUnicodeString(&DeviceName,L"\\Device\\"CONTROL_DEVICE_NAME);
        status=IoCreateDevice(DriverObject,sizeof(DEVICE_EXT_DATA),&DeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,&g_pControlDevice);
        if(!NT_SUCCESS(status))
        {
            KdPrint(("Failed create control device %08X",status));
            break;
        }
        //init extension
        DEVICE_EXT_DATA* pExt=(DEVICE_EXT_DATA*)g_pControlDevice->DeviceExtension;
        memset(pExt,0,sizeof(DEVICE_EXT_DATA));
        new (&pExt->HiddenPathsMatcher) CPathsMatcher();
        pExt->RegistryPath.MaximumLength=RegistryPath->Length+(wcslen(PARAMETERS_STRING)+1)*sizeof(WCHAR);
        pExt->RegistryPath.Buffer=(WCHAR*)ExAllocatePoolWithTag(PagedPool,pExt->RegistryPath.MaximumLength,POOL_TAG);
        if(!pExt->RegistryPath.Buffer)
        {
            KdPrint(("Failed allocate string buffer"));
            status=STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        memcpy(pExt->RegistryPath.Buffer,RegistryPath->Buffer,RegistryPath->Length);
        memcpy(((UCHAR*)pExt->RegistryPath.Buffer)+RegistryPath->Length,PARAMETERS_STRING,(wcslen(PARAMETERS_STRING)+1)*sizeof(WCHAR));
        pExt->RegistryPath.Length=pExt->RegistryPath.MaximumLength;
        g_pControlDevice->Flags&=~DO_DEVICE_INITIALIZING;
        UNICODE_STRING SymlinkName;
        RtlInitUnicodeString(&SymlinkName,L"\\DosDevices\\"CONTROL_DEVICE_NAME);
        status=IoCreateSymbolicLink(&SymlinkName,&DeviceName);
        if(!NT_SUCCESS(status))
        {
            KdPrint(("Failed create symbolic link %08X",status));
            break;
        }
        status=FltStartFiltering (g_pFilterData);
        if(!NT_SUCCESS(status))
        {
            KdPrint(("Failed start filtering %08X",status));
            break;
        }
        status=LoadConfiguration(g_pControlDevice);
    }
    while(0);
    if(!NT_SUCCESS(status))
    {
        if(g_pFilterData)
            FltUnregisterFilter(g_pFilterData);
        g_pFilterData=NULL;
        if(g_pControlDevice)
        {
            DEVICE_EXT_DATA* pExt=(DEVICE_EXT_DATA*)g_pControlDevice->DeviceExtension;
            pExt->HiddenPathsMatcher.Destroy();
            if(pExt->RegistryPath.Buffer)
                ExFreePool(pExt->RegistryPath.Buffer);
            IoDeleteDevice(g_pControlDevice);
        }
        g_pControlDevice=NULL;
        //if we not created symlink yet, nothing bad happens
        UNICODE_STRING SymlinkName;
        RtlInitUnicodeString(&SymlinkName,L"\\DosDevices\\"CONTROL_DEVICE_NAME);
        IoDeleteSymbolicLink(&SymlinkName);
    }
    return status;
}

void DriverUnload(DRIVER_OBJECT* pDrvObj)
{
    KdPrint(("Unloading driver.."));
    UNICODE_STRING SymlinkName;
    RtlInitUnicodeString(&SymlinkName,L"\\DosDevices\\"CONTROL_DEVICE_NAME);
    IoDeleteSymbolicLink(&SymlinkName);
    if(g_pControlDevice)
    {
        DEVICE_EXT_DATA* pExt=(DEVICE_EXT_DATA*)g_pControlDevice->DeviceExtension;
        pExt->HiddenPathsMatcher.Destroy();
        if(pExt->RegistryPath.Buffer)
            ExFreePool(pExt->RegistryPath.Buffer);
        IoDeleteDevice(g_pControlDevice);
    }
    if(g_pFilterData!=NULL)
    {
       FltUnregisterFilter(g_pFilterData);
    }
}
/**
Returns true in case when all files in directory must be 
fileterd, in other case some files still be present after filtering
*/
bool  FilterDirInfo(ULONG ulSize,
            void* pData,
            size_t FileNameLengthOffset,
            size_t FileNameOffset,
            const CPathsMatcher* pMatcher,
            const UNICODE_STRING* pParentDirInfo)
{
    UCHAR* pStart=(UCHAR*)pData;
    UCHAR* pCur=pStart;
    UNICODE_STRING Buf;
    Buf.MaximumLength=pParentDirInfo->MaximumLength+100;
    Buf.Buffer=(WCHAR*)ExAllocatePoolWithTag(PagedPool,Buf.MaximumLength,POOL_TAG);
    if(!Buf.Buffer)
    {
        KdPrint(("Failed allocate filtering buffer"));
        return false;
    }
    memcpy(Buf.Buffer,pParentDirInfo->Buffer,pParentDirInfo->Length);
    Buf.Buffer[pParentDirInfo->Length/sizeof(WCHAR)]=L'\\';
    ULONG ulBytesToSkip=0;
    UCHAR* pPrevEntry=NULL;
    bool bEmptySet=false;
    while((pCur-pStart)<(long)ulSize)
    {
        ULONG ulNextEntryOffset=*(ULONG*)pCur;
        ULONG ulNameLen=*(ULONG*)(pCur+FileNameLengthOffset);
        WCHAR* szName=(WCHAR*)(pCur+FileNameOffset);
        //skip special entries "." and ".." can not be filtered out
        if(ulNameLen==2 && szName[0]==L'.' ||
            ulNameLen==4 && szName[0]==L'.' && szName[1]==L'.')
        {
            pPrevEntry=pCur;
        }
        else
        {//not a special entry - check to filter
            //ensure we have enough space in buffer
            if((pParentDirInfo->Length+ulNameLen+1)>Buf.MaximumLength)
            {
                ExFreePool(Buf.Buffer);
                Buf.MaximumLength=(USHORT)(pParentDirInfo->Length+ulNameLen+1);
                Buf.Buffer=(WCHAR*)ExAllocatePoolWithTag(PagedPool,Buf.MaximumLength,POOL_TAG);
                if(!Buf.Buffer)
                {
                    KdPrint(("Failed allocate filtering buffer"));
                    return false;
                }
                memcpy(Buf.Buffer,pParentDirInfo->Buffer,pParentDirInfo->Length);
                Buf.Buffer[pParentDirInfo->Length/sizeof(WCHAR)]=L'\\';
            }
            memcpy(((UCHAR*)Buf.Buffer)+pParentDirInfo->Length+sizeof(WCHAR),szName,ulNameLen);
            Buf.Length=(USHORT)(pParentDirInfo->Length+ulNameLen+sizeof(WCHAR));
            if(pMatcher->IsPathMatches(&Buf))
            {
                KdPrint(("Filtering out %wZ",&Buf));
                if(pPrevEntry)
                {//ok, prev entry exist - modify nextEntryOffset in it
                    if(ulNextEntryOffset!=0)
                        *((ULONG*)pPrevEntry)+=ulNextEntryOffset;
                    else
                        *((ULONG*)pPrevEntry)=0;//mark prev entry as last
                }
                else
                {//this is either first entry in block, or all previous entries are filtered
                    if(ulNextEntryOffset==0)
                    {//this is last entry, and it and all entries before it are filtered
                        bEmptySet=true;
                    }
                    ulBytesToSkip+=ulNextEntryOffset;
                }
            }
            else
                pPrevEntry=pCur;
        }
        pCur+=ulNextEntryOffset;
        if(ulNextEntryOffset==0)
            break;
    }
    ExFreePool(Buf.Buffer);
    if(bEmptySet)
    {
        return true;
    }
    else
    {
        if(ulBytesToSkip!=0)
        {
            KdPrint(("copying to skip %d bytes",ulBytesToSkip));
            memcpy(pStart,pStart+ulBytesToSkip,ulSize-ulBytesToSkip);
        }
    }
    return false;
}

FLT_POSTOP_CALLBACK_STATUS FLTAPI PostDirectoryControl(PFLT_CALLBACK_DATA Data,PCFLT_RELATED_OBJECTS FltObjects,PVOID CompletionContext,FLT_POST_OPERATION_FLAGS Flags)
{
    ULONG irql=KeGetCurrentIrql();
    if (!NT_SUCCESS( Data->IoStatus.Status)) 
    {
        KdPrint(("Not OK dir control result %08X",Data->IoStatus.Status));
        return FLT_POSTOP_FINISHED_PROCESSING;
    }
    if(Data->Iopb->MinorFunction!=IRP_MN_QUERY_DIRECTORY)
    {
        KdPrint(("Not a query directory %d",Data->Iopb->MinorFunction));
        return FLT_POSTOP_FINISHED_PROCESSING;
    }
    FLT_FILE_NAME_INFORMATION *pFileInfo=NULL;
    NTSTATUS status=FltGetFileNameInformation(Data,FLT_FILE_NAME_NORMALIZED|FLT_FILE_NAME_QUERY_DEFAULT,&pFileInfo);
    if(!NT_SUCCESS(status))
    {
        KdPrint(("ERROR DIR CONTROL! Failed to get file info, status %08X",status));
        return FLT_POSTOP_FINISHED_PROCESSING;
    }
    KdPrint(("DIRECTORY %wZ",&pFileInfo->Name));
    ULONG ulSize=Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length;
    //we must use MDL where possible, since DirectoryBuffer can point to UM memory, which might be inaccessible
    void* pData=NULL;
    bool bNeedUnlockMdl=false;
    if(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress)
    {
        if(!(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress->MdlFlags & MDL_PAGES_LOCKED))
        {//MDL describves not locked pages, lock them
            __try
            {
                MmProbeAndLockPages(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress,KernelMode,IoModifyAccess);
                bNeedUnlockMdl=true;
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                KdPrint(("Failed lock MDL pages"));
                return FLT_POSTOP_FINISHED_PROCESSING;
            }
        }
        pData=MmGetSystemAddressForMdlSafe(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress,NormalPagePriority);
        if(!pData)
        {
            if(bNeedUnlockMdl)
            {
                MmUnlockPages(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress);
            }
            KdPrint(("Failed get System address for MDL"));
            return FLT_POSTOP_FINISHED_PROCESSING;
        }
    }
    else
    {
        pData=Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
    }
    DEVICE_EXT_DATA* pExt=(DEVICE_EXT_DATA*)g_pControlDevice->DeviceExtension;
    bool isEverythingFiltered=false;
    if(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass==FileBothDirectoryInformation)
    {
        isEverythingFiltered=FilterDirInfo(ulSize,
            pData,
            FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION,FileNameLength),
            FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION,FileName),
            &pExt->HiddenPathsMatcher,
            &pFileInfo->Name);
    }
    else if(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass==FileDirectoryInformation)
    {
        isEverythingFiltered=FilterDirInfo(ulSize,
            pData,
            FIELD_OFFSET(FILE_DIRECTORY_INFORMATION,FileNameLength),
            FIELD_OFFSET(FILE_DIRECTORY_INFORMATION,FileName),
            &pExt->HiddenPathsMatcher,
            &pFileInfo->Name);
    }
    else if(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass==FileFullDirectoryInformation)
    {
        isEverythingFiltered=FilterDirInfo(ulSize,
            pData,
            FIELD_OFFSET(FILE_FULL_DIR_INFORMATION ,FileNameLength),
            FIELD_OFFSET(FILE_FULL_DIR_INFORMATION ,FileName),
            &pExt->HiddenPathsMatcher,
            &pFileInfo->Name);
    }
    else if(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass==FileIdBothDirectoryInformation)
    {
        isEverythingFiltered=FilterDirInfo(ulSize,
            pData,
            FIELD_OFFSET(FILE_ID_BOTH_DIR_INFORMATION ,FileNameLength),
            FIELD_OFFSET(FILE_ID_BOTH_DIR_INFORMATION ,FileName),
            &pExt->HiddenPathsMatcher,
            &pFileInfo->Name);
    }
    else if(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass==FileIdFullDirectoryInformation)
    {
         isEverythingFiltered=FilterDirInfo(ulSize,
            pData,
            FIELD_OFFSET(FILE_ID_FULL_DIR_INFORMATION ,FileNameLength),
            FIELD_OFFSET(FILE_ID_FULL_DIR_INFORMATION ,FileName),
            &pExt->HiddenPathsMatcher,
            &pFileInfo->Name);
    }
    else if(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass==FileNamesInformation)
    {
         isEverythingFiltered=FilterDirInfo(ulSize,
            pData,
            FIELD_OFFSET(FILE_NAMES_INFORMATION ,FileNameLength),
            FIELD_OFFSET(FILE_NAMES_INFORMATION ,FileName),
            &pExt->HiddenPathsMatcher,
            &pFileInfo->Name);
    }
    FltReleaseFileNameInformation(pFileInfo);
    if(bNeedUnlockMdl)
    {
        MmUnlockPages(Data->Iopb->Parameters.DirectoryControl.QueryDirectory.MdlAddress);
    }
    if(isEverythingFiltered)
    {
        Data->IoStatus.Status=STATUS_NO_SUCH_FILE;
        Data->IoStatus.Information=0;
        FltSetCallbackDataDirty(Data);
    }
    return FLT_POSTOP_FINISHED_PROCESSING;
}

FLT_PREOP_CALLBACK_STATUS FLTAPI PreCreate(PFLT_CALLBACK_DATA Data,PCFLT_RELATED_OBJECTS FltObjects,PVOID *pCompletionContext)
{
    DEVICE_EXT_DATA* pData=(DEVICE_EXT_DATA*)g_pControlDevice->DeviceExtension;
    FLT_FILE_NAME_INFORMATION *pFileInfo=NULL;
    NTSTATUS status=FltGetFileNameInformation(Data,FLT_FILE_NAME_NORMALIZED|FLT_FILE_NAME_QUERY_DEFAULT,&pFileInfo);
    if(!NT_SUCCESS(status))
    {
        KdPrint(("ERROR DIR CONTROL! Failed to get file info, status %08X",status));
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }
    if(!pData->HiddenPathsMatcher.IsPathMatches(&pFileInfo->Name))
    {
        FltReleaseFileNameInformation(pFileInfo);
        return FLT_PREOP_SUCCESS_NO_CALLBACK;
    }
    FltReleaseFileNameInformation(pFileInfo);
    //fail request
    Data->IoStatus.Status=STATUS_NO_SUCH_FILE;
    Data->IoStatus.Information=0;
    return FLT_PREOP_COMPLETE;
}

NTSTATUS OnCreateClose(DEVICE_OBJECT* pDevice,IRP* pIrp)
{
    IO_STACK_LOCATION *pStackLoc=IoGetCurrentIrpStackLocation(pIrp);
    KdPrint(("OPEN/CLOSE REQUEST %d",pStackLoc->MajorFunction));
    pIrp->IoStatus.Status=STATUS_SUCCESS;
    IoCompleteRequest(pIrp,0);
    return STATUS_SUCCESS;
}

NTSTATUS OnDeviceControl(DEVICE_OBJECT* pDevice,IRP* pIrp)
{
    NTSTATUS status;
    IO_STACK_LOCATION* pStackLoc=IoGetCurrentIrpStackLocation(pIrp);
    switch(pStackLoc->Parameters.DeviceIoControl.IoControlCode)
    {
    case IOCTL_RELOAD_CONFIG:
        status=LoadConfiguration(pDevice);
        break;
    case IOCTL_ENABLE_HIDING:
        {
            if(pStackLoc->Parameters.DeviceIoControl.InputBufferLength<sizeof(ULONG))
                status=STATUS_INVALID_PARAMETER;
            else
            {
                ULONG ulData=*(ULONG*)pIrp->AssociatedIrp.SystemBuffer;
                DEVICE_EXT_DATA* pExt=(DEVICE_EXT_DATA*)pDevice->DeviceExtension;
                pExt->HiddenPathsMatcher.Enable(ulData!=0);
                status=STATUS_SUCCESS;
            }
            break;
        }
    default:
        status=STATUS_INVALID_DEVICE_REQUEST;
        break;
    }
    pIrp->IoStatus.Status=status;
    IoCompleteRequest(pIrp,0);
    return status;
}

NTSTATUS FLTAPI FilterUnload (FLT_FILTER_UNLOAD_FLAGS Flags)
{
    KdPrint(("FilterUnload called"));
    return STATUS_SUCCESS;
}

NTSTATUS NTAPI HidingEnabledProc(
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    )
{
    DEVICE_EXT_DATA* pExt=(DEVICE_EXT_DATA*)Context;
    if(ValueType!=REG_DWORD || ValueLength!=sizeof(ULONG))
    {
        KdPrint(("Invalid Hiding enabled value type %d",ValueType));
        return STATUS_UNSUCCESSFUL;
    }
    pExt->HiddenPathsMatcher.Enable((*(ULONG*)ValueData)!=0);
    return STATUS_SUCCESS;
}

NTSTATUS NTAPI HidingPathsProc(
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    )
{
    DEVICE_EXT_DATA* pExt=(DEVICE_EXT_DATA*)Context;
    if(ValueType!=REG_MULTI_SZ)
    {
        KdPrint(("Invalid Hiding paths value type %d",ValueType));
        return STATUS_UNSUCCESSFUL;
    }
    pExt->HiddenPathsMatcher.InitPaths((const WCHAR*)ValueData);
    return STATUS_SUCCESS;
}

NTSTATUS LoadConfiguration(DEVICE_OBJECT* pDevice)
{
    ULONG dwZero=0;
    WCHAR szEmpty[2];
    memset(szEmpty,0,sizeof(szEmpty));
    DEVICE_EXT_DATA* pExt=(DEVICE_EXT_DATA*)pDevice->DeviceExtension;
    RTL_QUERY_REGISTRY_TABLE QueryTable[]=
    {
        {HidingEnabledProc,0,HIDING_ENABLED_VALUE,NULL,REG_DWORD,&dwZero,sizeof(dwZero)},
        {HidingPathsProc,RTL_QUERY_REGISTRY_NOEXPAND,HIDDEN_PATHS_VALUE,NULL,REG_MULTI_SZ,szEmpty,sizeof(szEmpty)},
        {NULL,0,NULL,NULL,0,NULL,0}
    };
    return RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,pExt->RegistryPath.Buffer,QueryTable,pExt,NULL);
}
